/*
 * Copyright By ZATI
 * Copyright By 3a3c88295d37870dfd3b25056092d1a9209824b256c341f2cdc296437f671617
 * All rights reserved.
 *
 * If you are not the intended user, you are hereby notified that any use, disclosure, copying, printing, forwarding or
 * dissemination of this property is strictly prohibited. If you have got this file in error, delete it from your system.
 */

package cn.wzc.crystal.platform.golbal;

import cn.wzc.crystal.platform.i18n.I18nService;
import cn.wzc.crystal.platform.logic.LogicException;
import cn.wzc.crystal.platform.support.ApiResult;
import cn.wzc.crystal.platform.support.Code;
import feign.FeignException;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.core.MethodParameter;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.http.converter.HttpMessageConverter;
import org.springframework.http.server.ServerHttpRequest;
import org.springframework.http.server.ServerHttpResponse;
import org.springframework.lang.NonNull;
import org.springframework.security.access.AccessDeniedException;
import org.springframework.validation.BindException;
import org.springframework.validation.BindingResult;
import org.springframework.validation.FieldError;
import org.springframework.web.HttpRequestMethodNotSupportedException;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.MissingServletRequestParameterException;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseStatus;
import org.springframework.web.bind.annotation.RestControllerAdvice;
import org.springframework.web.servlet.mvc.method.annotation.ResponseBodyAdvice;

import java.util.List;

@Slf4j
@RestControllerAdvice
@RequiredArgsConstructor
public class GlobalApiHandler implements ResponseBodyAdvice<ApiResult> {

	private final I18nService i18nService;

	@Override
	public boolean supports(@NonNull MethodParameter returnType,
							@NonNull Class<? extends HttpMessageConverter<?>> converterType) {

		return ApiResult.class.equals(returnType.getParameterType());

	}

	@Override
	public ApiResult beforeBodyWrite(ApiResult body,
									 @NonNull MethodParameter returnType,
									 @NonNull MediaType selectedContentType,
									 @NonNull Class<? extends HttpMessageConverter<?>> selectedConverterType,
									 @NonNull ServerHttpRequest request,
									 @NonNull ServerHttpResponse response) {
		if (null == body) {
			return null;
		}


		return ApiResult.of(
			body.getCode(),
			i18nService.getMessage(body.getCode(), body.getMessage()),
			body.getData()
		);

	}

	/**
	 * 处理 -> 其他异常
	 *
	 * @param e 非系统代码抛出异常
	 * @return JSON描述错误结果
	 */
	@ExceptionHandler({Exception.class})
	public ResponseEntity<ApiResult> handlerException(Exception e) {
		GlobalErrorCode code;
		HttpStatus status;
		boolean printStack = true;

		// 方法不支持
		if (e instanceof HttpRequestMethodNotSupportedException) {
			code = GlobalErrorCode.ILLEGAL_REQUEST;
			status = HttpStatus.OK;
		}

		// 绑定错误
		else if (e instanceof BindException) {
			code = GlobalErrorCode.ILLEGAL_REQUEST;
			status = HttpStatus.OK;
		}

		// 缺少参数
		else if (e instanceof MissingServletRequestParameterException) {
			code = GlobalErrorCode.ILLEGAL_REQUEST;
			status = HttpStatus.OK;
		}

		// 权限问题
		else if (e instanceof AccessDeniedException) {
			code = GlobalErrorCode.ACCESS_DENIED;
			status = HttpStatus.FORBIDDEN;
		}


		// 其他情况
		else {
			code = GlobalErrorCode.UNKNOWN_ERROR;
			status = HttpStatus.INTERNAL_SERVER_ERROR;
		}

		if (status != HttpStatus.OK) {
			log.error("系统发异常", e);
		} else {
			log.error("系统发异常:{}", e.getLocalizedMessage());
		}

		final String codeValue = code.getCode();
		final String message = i18nService.getMessage(code.getCode(), code.getMessage());

		return ResponseEntity.status(status)
			.contentType(MediaType.APPLICATION_JSON)
			.body(ApiResult.of(codeValue, message));
	}

	/**
	 * 处理 -> 校验失败
	 *
	 * @param e 校验异常
	 * @return JSON描述错误结果
	 */
	@ResponseStatus(HttpStatus.OK)
	@ExceptionHandler({MethodArgumentNotValidException.class})
	public ApiResult handlerException(MethodArgumentNotValidException e) {
		final BindingResult result = e.getBindingResult();
		final List<FieldError> fieldErrors = result.getFieldErrors();
		final StringBuilder tempIllegalParamMsg = new StringBuilder();
		for (FieldError fieldError : fieldErrors) {
			tempIllegalParamMsg.append(fieldError.getField()).append(":").append(i18nService.getMessage(fieldError)).append(", ");
		}
		final Code code = GlobalErrorCode.ILLEGAL_REQUEST;
		return ApiResult.of(code, tempIllegalParamMsg.toString());
	}


	/**
	 * 处理 -> Feign 异常
	 *
	 * @param e Feign异常
	 * @return JSON描述错误结果
	 */
	@ExceptionHandler({FeignException.class})
	@ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
	public ResponseEntity<String> handlerException(FeignException e) {
		log.error("Feign接口调用异常", e);
		return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR)
			.contentType(MediaType.APPLICATION_JSON)
			.body(e.contentUTF8());
	}

	/**
	 * 处理 -> 业务异常
	 *
	 * @param e Feign异常
	 * @return JSON描述错误结果
	 */
	@ResponseStatus(HttpStatus.OK)
	@ExceptionHandler({LogicException.class})
	public ApiResult handlerException(LogicException e) {
		return ApiResult.of(e.getCode(), e.getMessage());
	}


}
